Архитектура ОС

Взаимное исключение. Семафоры

Архитектура ОС

План лекции

1. Критические секции и взаимное исключение

2. Аппаратные решения: TSL и SWAP

3. Программные решения: алгоритм Петерсона

4. Семафоры Дейкстры

5. Задача «производитель-потребитель»

6. Задача «читатели-писатели»

7. Мониторы Хоара

8. Практические примеры

Взаимное исключение. Семафоры
Архитектура ОС

1. Введение в синхронизацию

В многозадачных ОС несколько процессов одновременно обращаются к общим ресурсам. Это создаёт необходимость в механизмах синхронизации, обеспечивающих корректное выполнение программ.

  • Разделяемые ресурсы: память, файлы, устройства I/O
  • Параллельный доступ может привести к гонкам данных (race conditions)
  • Результат зависит от порядка выполнения процессов
  • Требуется координация доступа — взаимное исключение
Взаимное исключение. Семафоры
Архитектура ОС

Критическая секция

Критическая секция — участок кода, в котором процесс обращается к общим ресурсам и который не должен выполняться одновременно несколькими процессами.

Структура процесса:

  • Вход в критическую секцию (request entry)
  • Критическая секция (critical section)
  • Выход из критической секции (exit)
  • Остальной код (remainder section)
Взаимное исключение. Семафоры
Архитектура ОС

Условия Бернстайна

Для корректного параллельного выполнения необходимо выполнение трёх условий:

  • Взаимное исключение — если процесс в критической секции, другие не могут войти в свои критические секции
  • Прогресс — если ни один процесс не в критической секции, выбор процесса для входа не может откладываться бесконечно
  • Ограниченное ожидание — существует предел на число раз, когда другие процессы входят в критическую секцию после запроса данного процесса
Взаимное исключение. Семафоры
Архитектура ОС

Пример гонки данных

Два процесса увеличивают общую переменную counter++:

int counter = 0;

// Процесс A                    // Процесс B
reg_A = counter;                reg_B = counter;
reg_A = reg_A + 1;              reg_B = reg_B + 1;
counter = reg_A;                counter = reg_B;

Возможный результат: counter = 1 вместо counter = 2, если переключение контекста произошло между чтением и записью.

Взаимное исключение. Семафоры
Архитектура ОС

2. Аппаратные решения

Подходы, использующие аппаратные инструкции для обеспечения атомарности:

  • Отключение прерываний
  • Атомарные инструкции процессора:
    • TSL (Test-and-Set Lock)
    • SWAP (обмен значений)
  • Аппаратная поддержка гарантирует атомарность на уровне инструкций
Взаимное исключение. Семафоры
Архитектура ОС

Отключение прерываний

Самое простое аппаратное решение — запрет прерываний на время выполнения критической секции.

Преимущества:

  • Простота реализации
  • Гарантия атомарности на одном процессоре

Недостатки:

  • Не работает в многопроцессорных системах (SMP)
  • Потеря важных прерываний
  • Слишком широкий контроль над системой
  • Нарушение принципа минимальных привилегий
Взаимное исключение. Семафоры
Архитектура ОС

Команда TSL (Test-and-Set Lock)

Аппаратная команда, которая атомарно читает и устанавливает значение блокировки:

boolean TestAndSet(boolean *target) {
    boolean rv = *target;
    *target = TRUE;
    return rv;
}

Использование для входа в критическую секцию:

while (TestAndSet(&lock))
    ;
// критическая секция
lock = FALSE;
Взаимное исключение. Семафоры
Архитектура ОС

Использование TSL для взаимного исключения

Полное решение с TSL для nn процессов:

boolean lock = FALSE;

void enter_region() {
    while (TestAndSet(&lock))
        ;
}

void leave_region() {
    lock = FALSE;
}
  • TSL возвращает предыдущее значение и устанавливает TRUE
  • Если lock уже TRUE — процесс ждёт (spin lock)
  • Если lock был FALSE — процесс входит в секцию
Взаимное исключение. Семафоры
Архитектура ОС

Команда SWAP

Атомарно обменивает содержимое двух переменных:

void Swap(boolean *a, boolean *b) {
    boolean temp = *a;
    *a = *b;
    *b = temp;
}

Использование для взаимного исключения:

boolean lock = FALSE;

void enter_region() {
    boolean key = TRUE;
    do {
        Swap(&key, &lock);
    } while (key == TRUE);
}

void leave_region() {
    lock = FALSE;
}
Взаимное исключение. Семафоры
Архитектура ОС

3. Алгоритм Петерсона

Классический программный алгоритм для двух процессов, обеспечивающий взаимное исключение без специальной аппаратной поддержки.

Идея:

  • Каждый процесс объявляет о желании войти в критическую секцию
  • Флаг turn указывает, чья очередь
  • Процесс ждёт, пока другой находится в критической секции или не его очередь
Взаимное исключение. Семафоры
Архитектура ОС

Реализация алгоритма Петерсона

#define FALSE 0
#define TRUE  1
#define N     2

int turn;
int interested[N];

void enter_region(int process) {
    int other = 1 - process;
    interested[process] = TRUE;
    turn = process;
    while (turn == process && interested[other] == TRUE)
        ;
}

void leave_region(int process) {
    interested[process] = FALSE;
}
Взаимное исключение. Семафоры
Архитектура ОС

Корректность алгоритма Петерсона

Алгоритм удовлетворяет всем трём условиям:

  • Взаимное исключение — два процесса не могут одновременно войти: turn допускает только одного
  • Прогресс — процесс, не находящийся в критической секции, не мешает другому войти (interested = FALSE)
  • Ограниченное ожидание — после выхода процесс уступает очередь (turn = other)

Ограничение: первоначальный вариант работает только для двух процессов. Обобщение — алгоритм пекарни Лампорта.

Взаимное исключение. Семафоры
Архитектура ОС

4. Семафоры Дейкстры

Семафор — синхронизирующий механизм, предложенный Э. Дейкстрой в 1965 году.

Целочисленная переменная с двумя атомарными операциями:

  • P (wait, down): уменьшает значение на 1; если результат отрицателен — процесс блокируется
  • V (signal, up): увеличивает значение на 1; если есть заблокированные процессы — один разблокируется
P(S):  wait(S > 0);  S = S - 1;
V(S):  S = S + 1;  wakeup_one();
Взаимное исключение. Семафоры
Архитектура ОС

Реализация семафора

typedef struct {
    int value;
    struct process *list;
} semaphore;

void P(semaphore *s) {
    s->value--;
    if (s->value < 0) {
        add this process to s->list;
        block();
    }
}

void V(semaphore *s) {
    s->value++;
    if (s->value <= 0) {
        remove a process P from s->list;
        wakeup(P);
    }
}

Значение семафора может быть отрицательным — модуль равен числу заблокированных процессов.

Взаимное исключение. Семафоры
Архитектура ОС

Типы семафоров

Бинарные семафоры (мьютексы):

  • Принимают значения 0 и 1
  • Используются для взаимного исключения
  • Владелец — процесс, захвативший мьютекс
  • Простой и эффективный механизм

Считающие семафоры:

  • Принимают неотрицательные целые значения
  • Управляют доступом к ресурсам с несколькими экземплярами
  • Начальное значение = количество ресурсов
  • Подходят для пулов ресурсов
Взаимное исключение. Семафоры
Архитектура ОС

Использование семафоров для взаимного исключения

semaphore mutex = 1;

// Процесс i
while (TRUE) {
    P(&mutex);
    // критическая секция
    V(&mutex);
    // остальной код
}
  • Начальное значение mutex = 1 — один процесс может войти
  • Операция P уменьшает до 0 — остальные ждут
  • Операция V освобождает и будит ожидающего
Взаимное исключение. Семафоры
Архитектура ОС

5. Задача «производитель-потребитель»

Один или несколько производителей создают данные и помещают их в буфер фиксированного размера. Один или несколько потребителей извлекают данные из буфера.

Условия:

  • Производитель ждёт, если буфер полон
  • Потребитель ждёт, если буфер пуст
  • Доступ к буферу — взаимное исключение
Взаимное исключение. Семафоры
Архитектура ОС

Решение: производитель

#define N 100
semaphore mutex = 1;
semaphore empty = N;
semaphore full  = 0;

void producer() {
    while (TRUE) {
        produce_item();
        P(&empty);
        P(&mutex);
        enter_item();
        V(&mutex);
        V(&full);
    }
}
  • empty — свободных слотов (начальное N)
  • full — занятых слотов (начальное 0)
  • mutex — защита доступа к буферу
Взаимное исключение. Семафоры
Архитектура ОС

Решение: потребитель

void consumer() {
    while (TRUE) {
        P(&full);
        P(&mutex);
        remove_item();
        V(&mutex);
        V(&empty);
        consume_item();
    }
}

Важно: порядок операций P имеет значение!

  • Сначала P(&full), потом P(&mutex) — иначе взаимная блокировка
  • Производитель: сначала P(&empty), потом P(&mutex)
Взаимное исключение. Семафоры
Архитектура ОС

Почему важен порядок P-операций

Неверный порядок (потребитель):

P(&mutex);    // захватил мьютекс
P(&full);     // буфер пуст → блокировка
              // держит mutex → Producer не войдёт
              // DEADLOCK!

Верный порядок:

P(&full);     // ждёт данные, не держит mutex
P(&mutex);    // захватывает только когда есть что читать
Взаимное исключение. Семафоры
Архитектура ОС

6. Задача «читатели-писатели»

Несколько процессов-читателей и писателей имеют доступ к общей базе данных.

Условия:

  • Несколько читателей могут читать одновременно
  • Только один писатель может писать в любой момент
  • Когда писатель пишет — никто не читает
  • Не должно быть бесконечного откладывания
Взаимное исключение. Семафоры
Архитектура ОС

Решение: приоритет читателей

Читатели не ждут, пока есть хотя бы один активный читатель:

semaphore mutex   = 1;
semaphore rw_mutex = 1;
int read_count = 0;
  • mutex — защита read_count
  • rw_mutex — эксклюзивный доступ для записи (и первого/последнего читателя)
  • Первый читатель захватывает rw_mutex, последний — освобождает
Взаимное исключение. Семафоры
Архитектура ОС

Читатель (приоритет читателей)

void reader() {
    while (TRUE) {
        P(&mutex);
        read_count++;
        if (read_count == 1)
            P(&rw_mutex);
        V(&mutex);

        // чтение

        P(&mutex);
        read_count--;
        if (read_count == 0)
            V(&rw_mutex);
        V(&mutex);
    }
}
Взаимное исключение. Семафоры
Архитектура ОС

Писатель (приоритет читателей)

void writer() {
    while (TRUE) {
        P(&rw_mutex);

        // запись

        V(&rw_mutex);
    }
}

Проблема приоритета читателей: писатель может голодать, если читатели приходят непрерывно и read_count никогда не становится 0.

Взаимное исключение. Семафоры
Архитектура ОС

Решение: приоритет писателей

Чтобы избежать голодания писателей, вводится дополнительный семафор w_mutex:

semaphore mutex    = 1;
semaphore rw_mutex = 1;
semaphore w_mutex  = 1;
int read_count  = 0;
int write_count = 0;
  • w_mutex — блокирует новых читателей, когда есть ожидающий писатель
  • Писатель захватывает mutex через write_count (как читатель — через read_count)
Взаимное исключение. Семафоры
Архитектура ОС

Читатель (приоритет писателей)

void reader() {
    while (TRUE) {
        P(&w_mutex);
        P(&mutex);
        read_count++;
        if (read_count == 1)
            P(&rw_mutex);
        V(&mutex);
        V(&w_mutex);

        // чтение

        P(&mutex);
        read_count--;
        if (read_count == 0)
            V(&rw_mutex);
        V(&mutex);
    }
}
Взаимное исключение. Семафоры
Архитектура ОС

Писатель (приоритет писателей)

void writer() {
    while (TRUE) {
        P(&w_mutex);
        write_count++;
        if (write_count == 1)
            P(&mutex);
        V(&w_mutex);

        P(&rw_mutex);
        // запись
        V(&rw_mutex);

        P(&w_mutex);
        write_count--;
        if (write_count == 0)
            V(&mutex);
        V(&w_mutex);
    }
}
Взаимное исключение. Семафоры
Архитектура ОС

Сравнение подходов «читатели-писатели»

Приоритет читателей:

  • Читатели не ждут друг друга
  • Высокая пропускная способность чтения
  • Писатель может голодать
  • Простая реализация

Приоритет писателей:

  • Писатель блокирует новых читателей
  • Запись гарантированно выполняется
  • Читатель может голодать
  • Дополнительный семафор w_mutex
Взаимное исключение. Семафоры
Архитектура ОС

7. Мониторы Хоара

Монитор — высокоуровневый механизм синхронизации, объединяющий данные, операции над ними и синхронизацию в одну структуру.

Характеристики:

  • Содержит разделяемые данные
  • Определяет операции (процедуры) над этими данными
  • Обеспечивает взаимное исключение — только один процесс выполняет процедуру монитора
  • Использует переменные условия для синхронизации
Взаимное исключение. Семафоры
Архитектура ОС

Переменные условия в мониторе

Переменные условия (condition variables) используются для блокировки процессов, когда условия не выполнены:

  • wait(c) — процесс блокируется на переменной условия c и освобождает монитор
  • signal(c) — разбуждает один из процессов, ожидающих на c

Отличие от семафоров:

  • Переменные условия не накапливают сигналы
  • signal без ожидающих — теряется (без эффекта)
  • Взаимное исключение встроено в монитор
Взаимное исключение. Семафоры
Архитектура ОС

Монитор: задача «читатели-писатели»

monitor ReaderWriter {
    int read_count = 0;
    boolean writing = FALSE;
    condition ok_read, ok_write;

    void begin_read() {
        while (writing)
            wait(ok_read);
        read_count++;
        signal(ok_read);
    }

    void end_read() {
        read_count--;
        if (read_count == 0)
            signal(ok_write);
    }
}
Взаимное исключение. Семафоры
Архитектура ОС

Монитор: писатель

void begin_write() {
    while (writing || read_count > 0)
        wait(ok_write);
    writing = TRUE;
}

void end_write() {
    writing = FALSE;
    signal(ok_read);
    signal(ok_write);
}

Преимущества монитора:

  • Взаимное исключение — автоматически
  • Код проще и менее подвержен ошибкам
  • signal будит один процесс; broadcast — все ожидающие
Взаимное исключение. Семафоры
Архитектура ОС

Семафоры vs Мониторы

Семафоры:

  • Низкоуровневый примитив
  • Разработчик управляет блокировками вручную
  • Легко допустить ошибку (забыть V)
  • Работают в любом контексте

Мониторы:

  • Высокоуровневая абстракция
  • Взаимное исключение автоматически
  • Переменные условия безопаснее
  • Ограничены рамками монитора
Взаимное исключение. Семафоры
Архитектура ОС

8. Проблемы синхронизации

Голодание (Starvation):

  • Процесс не получает ресурсы из-за несправедливой политики
  • Решение: справедливые алгоритмы (FIFO, старение)

Взаимная блокировка (Deadlock):

  • Несколько процессов заблокированы, ожидая друг друга
  • Требует предотвращения, обнаружения или разрешения

Условия Коффмана для deadlock:

  • Взаимное исключение
  • Удержание и ожидание
  • Нет вытеснения
  • Циклическое ожидание
Взаимное исключение. Семафоры
Архитектура ОС

Принципы проектирования синхронизации

  • Корректность — гарантия отсутствия гонок и ошибок синхронизации
  • Эффективность — минимизация времени ожидания и накладных расходов
  • Справедливость — предотвращение голодания, равный доступ к ресурсам
  • Масштабируемость — работоспособность при увеличении числа процессов
Взаимное исключение. Семафоры
Архитектура ОС

9. Практические примеры

Семафоры и мьютексы в реальных системах:

  • System V IPCsemget, semop, semctl
  • POSIXsem_open, sem_wait, sem_post
  • pthreadpthread_mutex_lock/unlock
  • Linux kernelmutex, spinlock, rw_semaphore
Взаимное исключение. Семафоры
Архитектура ОС

Семафоры в UNIX (System V)

#include <sys/sem.h>

int semid = semget(IPC_PRIVATE, 1, 0666 | IPC_CREAT);
semctl(semid, 0, SETVAL, 1);

struct sembuf op_p = {0, -1, 0};
struct sembuf op_v = {0,  1, 0};

semop(semid, &op_p, 1);
// критическая секция
semop(semid, &op_v, 1);
Взаимное исключение. Семафоры
Архитектура ОС

Мьютексы в pthread

#include <pthread.h>

pthread_mutex_t mutex;

void init() {
    pthread_mutex_init(&mutex, NULL);
}

void critical_section() {
    pthread_mutex_lock(&mutex);
    // критическая секция
    pthread_mutex_unlock(&mutex);
}

void cleanup() {
    pthread_mutex_destroy(&mutex);
}
Взаимное исключение. Семафоры
Архитектура ОС

Условные переменные в pthread

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t  cond  = PTHREAD_COND_INITIALIZER;

// Поток-ожидание
pthread_mutex_lock(&mutex);
while (!condition)
    pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);

// Поток-уведомитель
pthread_mutex_lock(&mutex);
condition = TRUE;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
Взаимное исключение. Семафоры
Архитектура ОС

Иерархия примитивов синхронизации

Уровень Примитив Сложность
Аппаратный TSL, SWAP, CAS Низкая (spin lock)
Ядерный Семафоры Средняя
Ядерный Мьютексы Средняя
Языковой Мониторы Высокая
Библиотечный pthread, std::mutex Высокая
Взаимное исключение. Семафоры
Архитектура ОС

Перспективы развития

  • Безблокирующие алгоритмы (lock-free, wait-free) — повышение производительности
  • Адаптивные механизмы — учёт характеристик нагрузки
  • Аппаратная поддержка — улучшение эффективности (Intel TSX, ARM LSE)
  • Формальная верификация — доказательная корректность алгоритмов
Взаимное исключение. Семафоры
Архитектура ОС

Заключение

Механизмы взаимного исключения и синхронизации — фундамент надёжных многозадачных систем.

Развитие идёт от простых к сложным:

  1. Аппаратные инструкции → Семафоры → Мониторы
  2. Низкоуровневые примитивы → Высокоуровневые абстракции
  3. Ручное управление → Автоматическая синхронизация
Взаимное исключение. Семафоры
Архитектура ОС

Ключевые выводы лекции

  • Критическая секция требует выполнения трёх условий Бернстайна

  • TSL и SWAP — аппаратные атомарные инструкции

  • Алгоритм Петерсона — программное решение для двух процессов

  • Семафоры — универсальный механизм с операциями P и V

  • Задачи «производитель-потребитель» и «читатели-писатели» — классика синхронизации

  • Мониторы Хоара — высокоуровневая альтернатива семафорам

  • Порядок P-операций критичен для предотвращения deadlock

  • Выбор примитива зависит от задачи и контекста

Взаимное исключение. Семафоры
Архитектура ОС

Вопросы для самоконтроля

  1. Сформулируйте три условия корректного решения задачи о критической секции.
  2. Что такое гонка данных? Приведите пример.
  3. Как работает команда TSL и почему она обеспечивает атомарность?
  4. Объясните алгоритм Петерсона и докажите его корректность.
  5. Что такое семафор и какие операции над ним определены?
  6. Чем бинарный семафор отличается от считающего?
  7. Решите задачу «производитель-потребитель» с помощью семафоров.
  8. В чём различие между приоритетом читателей и приоритетом писателей?
  9. Какие преимущества monitors дают по сравнению с семафорами?
  10. Перечислите условия Коффмана для возникновения взаимной блокировки.
Взаимное исключение. Семафоры
Архитектура ОС

Рекомендуемые ресурсы

Основная литература:

  1. Таненбаум Э., Бос Х. Современные операционные системы. 4-е изд. — Питер, 2015.
  2. Столлингс В. Операционные системы. 9-е изд. — Вильямс, 2018.

Дополнительная литература:

  1. Dijkstra E. W. Cooperating Sequential Processes. — 1965.
  2. Стивенс У. UNIX. Взаимодействие процессов. — Питер, 2003.
  3. Herlihy M., Shavit N. The Art of Multiprocessor Programming. — Morgan Kaufmann, 2012.
Взаимное исключение. Семафоры

Озвучьте план лекции. Подчеркните, что начнём с низкоуровневых аппаратных решений и постепенно поднимемся к высокоуровневым абстракциям.

Начинаем с мотивации: зачем нужна синхронизация в многозадачных системах. Подчеркните, что гонки данных — не теоретическая проблема, а реальная угроза корректности программ.

Ключевое определение лекции. Нарисуйте схему: вход → критическая секция → выход → остаток. Именно эту структуру мы будем защищать всеми механизмами.

Три обязательных условия корректности. Подчеркните: все три должны выполняться одновременно. Аналогия — дверь в комнату: только один внутри, нельзя запереть навсегда, нельзя ждать бесконечно.

Конкретный пример. Покажите пошагово, как переключение контекста между чтением и записью приводит к потере обновления. Это ключевой мотивирующий пример всей лекции.

Переходим к аппаратным подходам. Объясните: процессор предоставляет базовые атомарные операции, на которых строятся все высокоуровневые примитивы синхронизации.

Простейший метод, но с серьёзными ограничениями. Подчеркните: в многопроцессорных SMP-системах отключение прерываний на одном ядре не мешает другому ядру обратиться к общим данным.

TSL — ключевая аппаратная инструкция. Объясните: атомарность означает, что чтение и запись выполняются как одна неразрывная операция — процессор гарантирует это аппаратно.

Покажите, как TSL строит spin lock — цикл активного ожидания. Процесс расходует процессорное время, пока ждёт — это главное ограничение подхода.

Альтернатива TSL. Swap атомарно обменивает значения, а логика ожидания реализуется через локальную переменную key в регистре процесса.

Переходим к чисто программным решениям. Алгоритм Петерсона не требует специальных аппаратных инструкций — только обычные чтение и запись в память.

Разберите код построчно. Ключевой момент: строка turn = process — процесс уступает очередь, а условие while проверяет и очередь, и флаг другого процесса.

Докажите корректность по трём условиям. Упомяните: для n процессов существует алгоритм пекарни Лампорта, но он значительно сложнее.

Центральная тема лекции. Эдсгер Дейкстра предложил семафоры в 1965 году — элегантная абстракция, скрывающая детали ожидания и пробуждения процессов.

Обратите внимание: отрицательное значение семафора — не ошибка, а число заблокированных процессов. Список ожидания обеспечивает пробуждение в порядке FIFO.

Два основных типа. Мьютекс — частный случай бинарного семафора. Считающий семафор удобен для управления пулами ресурсов с несколькими экземплярами.

Простейший паттерн: P перед критической секцией, V после. Начальное значение 1 разрешает вход одному процессу, остальные ждут.

Классическая задача синхронизации. Три семафора: mutex для защиты буфера, empty и full для координации состояний буфера между производителем и потребителем.

Разберите порядок операций. Производитель сначала ждёт пустой слот через P(&empty), потом захватывает мьютекс, заполняет слот и сигнализирует потребителю через V(&full).

Подчеркните: порядок P-операций критичен. Сначала проверяем наличие данных, потом захватываем мьютекс — иначе возможен взаимоблокировка.

Ключевой слайд. Покажите, как неправильный порядок приводит к deadlock: потребитель держит мьютекс и ждёт данные, а производитель не может положить данные — мьютекс занят.

Вторая классическая задача. Ключевое отличие: несколько читателей могут работать одновременно, но писателю нужен эксклюзивный доступ.

Объясните идею: первый читатель захватывает rw_mutex и блокирует писателей, последний читатель отпускает. Переменная read_count отслеживает число активных читателей.

Разберите код: первый читатель блокирует писателей через P(&rw_mutex), последний — разблокирует. Обратите внимание на защиту read_count мьютексом.

Писатель просто захватывает rw_mutex. Подчеркните проблему: если читатели приходят непрерывно и read_count не падает до нуля, писатель будет голодать бесконечно.

Решаем проблему голодания писателей. Дополнительный семафор w_mutex блокирует новых читателей, когда есть ожидающий писатель — это даёт писателю приоритет.

Обратите внимание: читатель сначала проходит через w_mutex, который может быть заблокирован ожидающим писателем. Именно это обеспечивает приоритет записи.

Двухфазная структура: регистрация через write_count и блокировка rw_mutex. Первый писатель блокирует читателей, последний — разблокирует.

Подведите итог: оба подхода решают задачу, но с разными приоритетами. Компромисс неизбежен — либо голодает писатель, либо читатель.

Переходим к высокоуровневым абстракциям. Монитор — как класс в ООП: данные + методы + встроенная синхронизация. Взаимное исключение гарантируется автоматически.

Ключевое отличие от семафоров: сигнал без ожидающих теряется бесследно. Это делает мониторы безопаснее — нельзя случайно накопить лишние разблокировки.

Сравните с решением через семафоры — код значительно проще. Взаимное исключение встроено, остаётся только логика ожидания условия.

Обратите внимание: signal(ok_read) и signal(ok_write) будят и читателей, и писателей. Взаимное исключение гарантируется монитором — не нужно думать о мьютексах.

Подведите итог сравнения. Семафоры мощнее и гибче, но мониторы безопаснее и проще. На практике мониторы предпочтительнее, где это возможно.

Две ключевые проблемы: голодание и взаимная блокировка. Условия Коффмана — чеклист из четырёх условий, необходимых для возникновения deadlock.

Четыре столпа проектирования. Подчеркните: на практике часто приходится искать баланс между эффективностью и справедливостью.

Переходим к реальным API. Покажите, что теоретические семафоры воплощены в конкретных системных вызовах и библиотеках, с которыми студенты будут работать.

System V IPC — классический интерфейс семафоров в UNIX. Объясните структуру sembuf: номер семафора, операция (+1/-1), флаги.

Наиболее частый паттерн в пользовательских программах. Подчеркните: мьютекс нужно инициализировать и обязательно уничтожать при завершении.

Покажите паттерн: мьютекс + условная переменная. Подчеркните, что проверка условия в цикле while, а не в if — защита от ложных пробуждений (spurious wakeup).

Обобщающая таблица. Покажите эволюцию: от аппаратных инструкций к библиотечным абстракциям. Чем выше уровень, тем безопаснее и проще использование.

Загляните в будущее: lock-free алгоритмы и аппаратная транзакционная память. Подчеркните: проблема синхронизации остаётся актуальной и активно развивается.

Подведите итог лекции: от TSL до мониторов — эволюция от простых к сложным. Каждый уровень абстракции решает проблемы предыдущего.

Резюме главных тезисов лекции. Обратите внимание студентов на вопросы для самоконтроля — они помогут закрепить материал.

Порекомендуйте студентам проработать каждый вопрос. Особенно важны вопросы 4, 7 и 10 — они проверяют глубокое понимание механизмов синхронизации.

Порекомендуйте Таненбаума как основной учебник, а статью Дейкстры 1965 года — для исторического контекста и глубокого понимания семафоров.